/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "IScene.h"
#include "core/core.h"
#include "Timer.h"
#include "Config.h"
#include <sstream>
#include "IDrawPrimitive.h"

IScene *g_pScene = NULL;

IScene* coreGetScene()
{
	return g_pScene;
}


IScene::IScene(void)
:	pRenderer(NULL),
	pInputHandler(NULL),
	pRootNode(NULL),
	pGuiRoot(NULL),
	bInitialized(false),
	bshowMouse(false),
	bCenterMouse(false),
	iMousePosX(0),
	iMousePosY(0),
	iMousePosZ(0),
	iLastMouseButton(-1),
	debuginfo(NULL),
	pPreloadImage(NULL),
	bShowFPS(false)
{
	// Get the current renderer
	pRenderer = coreGetRenderer();
	// Assign the input handler
	pInputHandler = coreGetInputHandler();
	// Create the root node
	pRootNode = new sg::SceneNode();
	pRootNode->m_bIsRootNode = true;

	m_vVisibleObjects.clear();
	m_vLightList.clear();

	// Read the value for displaying the frame counter
	Config* conf = coreGetConfig();
	if (conf->isDefined("g_showfps")) {
		bShowFPS = conf->getBoolean("g_showfps");
	}
}

IScene::~IScene(void)
{
	bInitialized = false;
	pRenderer = 0;
	pInputHandler = 0;
	pRootNode->destroy();

	// Delete all the lights
	for (std::list<ILight*>::iterator it = m_vLightList.begin(); it != m_vLightList.end(); ++it) {
		ILight* light = *it;
		DELETE_POINTER( light );
		it = m_vLightList.erase(it);
		if (it == m_vLightList.end()) 
			break;
	}

	debuginfo = NULL;
	DELETE_POINTER(pGuiRoot);
}

void IScene::draw2DElements()
{
	static std::ostringstream sstr;

	pRenderer->beginScene();

	if (pGuiRoot)
		pGuiRoot->draw();


	// Draw the mouse
	if (bshowMouse)
		pRenderer->drawMouse(iMousePosX, iMousePosY, bCenterMouse);

	if (bShowFPS && debuginfo) {
		// Draw some debug information
		sstr << "FPS: " << coreGetTimer()->getFPS();
		debuginfo->print(sstr.str(), coreGetMainWindow()->getWidth()-70, 10);
		sstr.str("");
		sstr.clear();
	}

	pRenderer->endScene();
}

bool IScene::preDraw()
{
	return pRenderer->prepareBuffer();
}

void IScene::drawScene()
{
	// Prepare drawing the scene
	if (!preDraw()) return;

	// Draw a gui
	if (pGuiRoot)
		pGuiRoot->update();

	if (bInitialized) {
		// Call the specialized draw function
		draw();
		// Draw 2D elements
		draw2DElements();
	} 
	else {
		drawPreload();
	}

	// Present the scene on screen
	postDraw();
}

void IScene::postDraw()
{
	// Display the back buffer
	pRenderer->swapBuffer();
}

void IScene::setPreloadImage(ITexture* texture)
{
	pPreloadImage = texture;
}

void IScene::drawPreload()
{
	if (pPreloadImage) {
		pRenderer->beginScene();
		pRenderer->clearScene();

		pPreloadImage->display();

		pRenderer->endScene();
	}
}

void IScene::showMouse(bool value, bool center)
{
	bCenterMouse = center;
	bshowMouse = value;
}

const bool IScene::isMouseShown() const
{
	return bshowMouse;
}

void IScene::processKeys() 
{
}

void IScene::processKeyDown(WPARAM wParam, LPARAM lParam)
{
}

void IScene::processKeyUp(WPARAM wParam, LPARAM lParam)
{
}

void IScene::processKey(UINT msg, WPARAM wParam, LPARAM lParam)
{
}

void IScene::processMouse(int mx, int my, int mz, int button)
{
	iMousePosX = mx;
	iMousePosY = my;
	iMousePosZ = mz;

	if (pGuiRoot) 
	{
		pGuiRoot->processMouse(mx, my, mz, button);		
		if (button != -1) 
		{
			if (iLastMouseButton != button) 
			{
					iLastMouseButton = button;
			}
		}
		else 
			iLastMouseButton = -1;
		
	}
}

void IScene::processMouseMotion(int relx, int rely, int relz, int button)
{	
	pRenderer->getCamera()->rotateAroundTarget( relx/100.0f, rely/100.0f );

	if (relz != 0) {
		math::Vector3 camDir = pRenderer->getCamera()->getTarget() - pRenderer->getCamera()->getPosition();
		camDir.normalize();

		pRenderer->getCamera()->setPosition( pRenderer->getCamera()->getPosition() + camDir * (relz / 100.0f));
	}
}

IRenderer* IScene::getRenderer() 
{
	return pRenderer;
}

IInputHandler* IScene::getInputHandler()
{
	return pInputHandler;
}

bool IScene::init()
{
	debuginfo = coreGetResourceFactory()->loadTrueTypeFont("Arial", 16);
	return true;
}

sg::SceneNode* IScene::getRoot()
{
	return pRootNode;
}

gui::Component* IScene::getGUIRoot() const
{
	return pGuiRoot;
}


bool IScene::isInitialized() const
{
	return bInitialized;
}

void IScene::setInitialized(bool value)
{
	bInitialized = value;
}

void IScene::destroy() 
{
	coreGetResourceFactory()->freeRes(debuginfo);
	//delete this;
}

void IScene::addLight(ILight *light)
{
	m_vLightList.push_back( light );
}

ILight* IScene::getLight(int pos)
{
	ILight* l = *m_vLightList.begin()+pos;
	return l;
}

void IScene::clearLights()
{
	for (std::list<ILight*>::iterator it = m_vLightList.begin(); it != m_vLightList.end(); ++it) {
		DELETE_POINTER( *it );
		it = m_vLightList.erase( it); 
		if (it == m_vLightList.end())
			return;
	}
}

void IScene::debugDrawLights()
{
	static IDrawPrimitive* lightSphere = IDrawPrimitive::CreateSphere(0.5f, 8, 8);

	math::Vector3 pos;
	for (std::list<ILight*>::iterator it = m_vLightList.begin(); it != m_vLightList.end(); ++it) {
		pRenderer->pushMatrix();
		pos = math::Vector3(&(*it)->getPosition().x);
		pRenderer->translate(pos);
		pRenderer->drawPrimitive(lightSphere);
		if ((*it)->getLightType() == LIGHT_DIRECTIONAL)
			pRenderer->drawLine(math::Vector3(0,0,0), -math::Vector3(&(*it)->getPosition().x));
		/*
		if ((*it)->getRange().x > 0) {
			pRenderer->setWireframe(true);
			pRenderer->drawSphere((*it)->getRange().x, (*it)->getDiffuseColor());
			pRenderer->setWireframe(false);
		}
		*/
		pRenderer->popMatrix();
	}
}

std::list<ILight*>* IScene::getLights()
{
	return &m_vLightList;
}

std::list<ILight*>* IScene::getVisibleLights()
{
	return &m_vVisibleLights;
}

void IScene::getCurrentMouseState( int state[4])
{
	state[0] = iMousePosX;
	state[1] = iMousePosY;
	state[2] = iMousePosZ;
	state[3] = iLastMouseButton;
}

void IScene::setGuiRoot( gui::Component *comp)
{
	pGuiRoot = comp;
}

void IScene::updateVisibleLights(ICamera* camera)
{
	static std::list<ILight*>::iterator it;
	static math::Vector3 maxrange(0.0f, 0.0f, 0.0f);
	// Clear all visible lists
	m_vVisibleLights.clear();

	// Go through all lights and push it, if it's visible
	for (it = m_vLightList.begin(); it != m_vLightList.end(); ++it) {
		if ((*it)->getRange() < maxrange) {
			// Push all lights with a range of -1.0f
			m_vVisibleLights.push_back(*it);
		} else {
			float min = max(max((*it)->getRange().x, (*it)->getRange().y), (*it)->getRange().z);
			if (camera->sphereInFrustum(model::BoundingSphere(math::Vector3(&(*it)->getPosition().x), min))) {
				m_vVisibleLights.push_back(*it);
			}
		}
	}
}
